home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Classes / 2.0_nxyPalette1.2 / src / NXYController.m < prev    next >
Encoding:
Text File  |  1992-01-24  |  15.6 KB  |  544 lines

  1.  
  2. /* Generated by Interface Builder */
  3.  
  4. #import "NXYController.h"
  5. #import "NXYView.h"        /*  only for initializeLegendBox */
  6. /* will get warning message from compiler if preceding line is commented out */
  7. #import <appkit/OpenPanel.h>
  8. #import <appkit/Matrix.h>
  9. #import <appkit/Cell.h>
  10. #import <math.h>
  11.  
  12. #define ALLOCSIZE 2048        /* used in malloc in readData */
  13.  
  14. @implementation NXYController
  15.  
  16. - (NXCoord *)xdata  { return x;}
  17. - (NXCoord **)ydata { return y;}
  18.  
  19. - (int)nPoints    { return npoints;}
  20. - (int)nCurves    { return ncurves;}
  21.  
  22. - (const char *)provideXtitle    {return [xTitle stringValueAt:0];}
  23. - (const char *)provideYtitle    {return [yTitle stringValueAt:0];}
  24. - (const char *)provideMaintitle    {return [mainTitle stringValueAt:0];}
  25.  
  26. - (const char *)provideCurveTitle:(int)aCurve
  27. {
  28.     return [legendForm stringValueAt:aCurve];
  29. }
  30.  
  31. - (const char *)provideLegendTitle
  32. {
  33.     return [legendTitle stringValueAt:0];
  34. }
  35.  
  36. - (BOOL) shouldDrawLegend
  37. {
  38.   if ( [legendOnOff state] ) return YES;
  39.   else return NO;
  40. }
  41. - (BOOL) shouldDrawLegendBox
  42. {
  43.   if ( [legendBoxOnOff state] ) return YES;
  44.   else return NO;
  45. }
  46.  
  47. - (BOOL) shouldDrawGrid
  48. {
  49.   if ( [gridOnOff state] ) return YES;
  50.   else return NO;
  51. }
  52.  
  53. - (BOOL) shouldDrawBox
  54. {
  55.   if ( [borderBoxOnOff state] ) return YES;
  56.   else return NO;
  57. }
  58.  
  59. - (BOOL) shouldMoveLegend
  60. {
  61.   if ( [legendMove state] ) return YES;
  62.   else return NO;
  63. }
  64.  
  65. - (BOOL) doZoom
  66. {
  67.   if ( [zoomOnOff state] ) return YES;
  68.   else return NO;
  69. }
  70.  
  71. - (BOOL) xaxisLog
  72. {
  73.   if ( [xLinLog state] ) return YES;
  74.   else return NO;
  75. }
  76.  
  77. - forceXaxisLinear
  78. {
  79.   [xLinLog setState:0];
  80.   [xLinLog display];
  81.   return self;
  82. }
  83.  
  84. - (BOOL) yaxisLog
  85. {
  86.   if ( [yLinLog state] ) return YES;
  87.   else return NO;
  88. }
  89.  
  90. - forceYaxisLinear
  91. {
  92.   [yLinLog setState:0];
  93.   [yLinLog display];
  94.   return self;
  95. }
  96.  
  97. - (int)providelinestyle:(int)aCurve
  98. {
  99.     int   row;
  100.     int   cellstate;
  101.  
  102.     for (row=0; row<N_LINE_STYLES; row++) {
  103.       cellstate = [ [lineMatrix cellAt:row :aCurve] state];
  104.       if (cellstate == 1) return row;
  105.     }
  106.     return 0;            /* for safety */
  107. }
  108.  
  109. - (int)providesymbolstyle:(int)aCurve
  110. {
  111.     int   row;
  112.     int   cellstate;
  113.  
  114.     for (row=0; row<N_SYMBOL_STYLES; row++) {
  115.       cellstate = [ [symbolMatrix cellAt:row :aCurve] state];
  116.       if (cellstate == 1) return row;
  117.     }
  118.     return 0;            /* for safety */
  119. }
  120.  
  121. - (int)providesymbolsize{  return [symbolSize selectedCol];}
  122. - (int)providelinethickness{  return [lineThickness selectedCol];}
  123.  
  124. - (float)provideXmin  {return [xMin floatValueAt:0];}
  125. - (float)provideXmax  {return [xMax floatValueAt:0];}
  126. - (float)provideXinc  {return [xInc floatValueAt:0];}
  127. - (float)provideYmin  {return [yMin floatValueAt:0];}
  128. - (float)provideYmax  {return [yMax floatValueAt:0];}
  129. - (float)provideYinc  {return [yInc floatValueAt:0];}
  130.  
  131. - resetXmin:(float)aNum { [xMin setFloatValue:aNum at:0]; return self; }
  132. - resetXmax:(float)aNum { [xMax setFloatValue:aNum at:0]; return self; }
  133. - resetXinc:(float)aNum { [xInc setFloatValue:aNum at:0]; return self; }
  134. - resetYmin:(float)aNum { [yMin setFloatValue:aNum at:0]; return self; }
  135. - resetYmax:(float)aNum { [yMax setFloatValue:aNum at:0]; return self; }
  136. - resetYinc:(float)aNum { [yInc setFloatValue:aNum at:0]; return self; }
  137.  
  138. - resetMinMax:sender
  139. {
  140.   [xMin setFloatValue:datamin.x at:0];
  141.   [xMax setFloatValue:datamax.x at:0];
  142.   [xInc setFloatValue:(datamax.x - datamin.x)/5.0 at:0];
  143.   [yMin setFloatValue:datamin.y at:0];
  144.   [yMax setFloatValue:datamax.y at:0];
  145.   [yInc setFloatValue:(datamax.y - datamin.y)/5.0 at:0];
  146.   return self;
  147. }
  148.  
  149. - drawPlotButton:(int)state
  150. {
  151.   if (state==0) {
  152.     [plotButton highlight:NO];    /* will display normal title */
  153.   }
  154.   if (state==1) {
  155.     [plotButton highlight:YES];    /* will display alternate title */
  156.   }
  157.   return self;
  158. }
  159.  
  160. - drawPlot:sender
  161. {
  162.     if (!x) return self;
  163.  
  164.     [self drawPlotButton:1];    /* display "Plotting" */
  165.     [canvas display];
  166.     [self drawPlotButton:0];    /* display "Plot" */
  167.     return self;
  168. }
  169.  
  170. // Allocate enough memory and read the data points
  171. /*
  172.  * WARNING: This code will go into an infinite loop if there is illegal
  173.  * crud in the data file (such as a comma, for example).  The culprit is
  174.  * the "fscanf" below; we will put up with this for now, but we should
  175.  * make this more robust.
  176.  */
  177. - (int) readData:(FILE *)aDataStream
  178. {
  179.     BOOL    inword = NO;
  180.     char    c;
  181.     int     j, size = ALLOCSIZE;
  182.     int     oldncurves = ncurves; /* for linestyle matrix column deleting */
  183.  
  184.     /* set plot button title "Reading" */
  185.     [plotButton setAltTitle:"Reading"];
  186.     [plotButton highlight:YES];
  187.     NXPing();            /* force plotButton redraw */
  188.  
  189.     /* First, free x and y here if there's already data in them */
  190.     if (x) {
  191.       free( (void *)x );
  192.       for (j = 0; j < ncurves; j++) {
  193.     free( (void *)(*(y+j)) );
  194.       }
  195.       free( (void *)y );
  196.     }
  197.  
  198.     /* Figure out the number of curves in the file by reading characters  */
  199.     /* until a newline is encountered; the number of curves is one less   */
  200.     /* than the number of words found.                                    */
  201.     /* For now we assume the input file is an ascii file.                 */
  202.     ncurves = -1;
  203.     while (1) {
  204.       c = getc(aDataStream);
  205.       if (c == '\n') {
  206.     break;            /* breaks out of while loop */
  207.       }
  208.       if ((inword==NO) && !(c==' ' || c=='\t')) {
  209.     ncurves++;
  210.     inword = YES;
  211.       }
  212.       if ((inword==YES) && (c==' ' || c=='\t')) {
  213.     inword = NO;
  214.       }
  215.     }
  216.     if (ncurves == -1) {    /* couldn't find "\n", give up */
  217.       return 0;
  218.     }
  219.  
  220.     /* Now read the data into memory */
  221.     rewind(aDataStream);
  222.     
  223.     y = (NXCoord **)malloc( ncurves * sizeof(NXCoord *) );
  224.     x = (NXCoord *)malloc( size * sizeof(NXCoord) );
  225.     for (j = 0; j < ncurves; j++) {
  226.       *(y+j) = (NXCoord *)malloc( size * sizeof(NXCoord) );
  227.     }
  228.     npoints = 0;
  229.     while(1) {
  230.       if( (fscanf(aDataStream, "%f", x+npoints)) == EOF ) {
  231.     break;            /* breaks out of the while loop */
  232.       }
  233.       for (j = 0; j < ncurves; j++) {
  234.     fscanf(aDataStream, "%f", *(y+j)+npoints);
  235.       }
  236.       npoints++;
  237.       if (npoints == size) {        /* get more memory */
  238.     size += ALLOCSIZE;
  239.     x = (NXCoord *)realloc(x, size * sizeof(NXCoord));
  240.     for (j = 0; j < ncurves; j++) {
  241.       *(y+j) = (NXCoord *)realloc( *(y+j), size * sizeof(NXCoord) );
  242.     }
  243.       }      
  244.     }
  245.     /* Adjust the lineMatrix, symbolMatrix, and legendForm */
  246.     [self adjustPanels:oldncurves :ncurves];
  247.  
  248.     /* reset plot button */
  249.     [plotButton setAltTitle:"Plotting"];
  250.     [plotButton highlight:NO];
  251.     return npoints;
  252. }
  253.  
  254. /* Might want to make sure INLINE_MATH is defined when math.h is included */
  255.  
  256. - checkLinLog
  257. {
  258.   /* Check x and y axes -- do a heuristic test for log/lin.
  259.    * The test employed comes from M. Merriam and xyplot.
  260.    */
  261.   double    scale, linsum, logsum;
  262.   register  double tmp;
  263.   int       i, j;
  264.  
  265.   /* First test x axis.  */
  266.   if (datamax.x > 0.0  &&  datamin.x > 0.0  && datamax.x != datamin.x) {
  267.     scale = fabs( (double)datamax.x - (double)datamin.x );
  268.     linsum = 0.0;
  269.     for (j=1; j<npoints; j++) {
  270.       tmp = ( (double)x[j]-(double)x[j-1] )/(double)scale; /* avoid overflow */
  271.       linsum += tmp*tmp;
  272.     }
  273.     scale = log10( (double)datamax.x/(double)datamin.x );
  274.                 /* what if datamax.x<datamin.x? */
  275.     logsum = 0.0;
  276.     for (i=1; i<npoints; i++) {
  277.       tmp = log10( (double)x[i]/(double)x[i-1] ) / scale;
  278.       logsum += tmp*tmp;
  279.     }
  280.     if (linsum < logsum) {
  281.       [xLinLog setState:0];    /* linear axis */
  282.     }
  283.     else {
  284.       [xLinLog setState:1];    /* logarithmic axis */
  285.     }
  286.   }
  287.   else {
  288.     [xLinLog setState:0];    /* linear */
  289.   }
  290.   /* Now test y axis */
  291.   if (datamax.y > 0.0  &&  datamin.y > 0.0  && datamax.y != datamin.y) {
  292.     scale = fabs( (double)datamax.y - (double)datamin.y );
  293.     linsum = 0.0;
  294.     for (j=0; j<ncurves; j++) {
  295.       for (i=1; i<npoints; i++) {
  296.     tmp = ( (double)*(*(y+j)+i) - (double)*(*(y+j)+i-1) )
  297.                         / scale; /* avoid overflow */
  298.     linsum += tmp*tmp;
  299.       }
  300.     }
  301.     scale = log10((double)datamax.y/(double)datamin.y);
  302.                 /* what if datamax.y<datamin.y? */
  303.     logsum = 0.0;
  304.     for (j=0; j<ncurves; j++) {
  305.       for (i=1; i<npoints;i++) {
  306.     tmp = log10( (double)*(*(y+j)+i)/(double)*(*(y+j)+i-1) ) / scale;
  307.     logsum += tmp*tmp;
  308.       }
  309.     }
  310.     if (linsum < logsum) {
  311.       [yLinLog setState:0];    /* linear axis */
  312.     }
  313.     else {
  314.       [yLinLog setState:1];    /* logarithmic axis */
  315.     }
  316.   }
  317.   else {
  318.     [yLinLog setState:0];    /* linear */
  319.   }
  320.   [xLinLog display];
  321.   [yLinLog display];
  322.   return self;
  323. }
  324.  
  325. - adjustPanels:(int)oldn :(int)newn
  326. {
  327.   /*
  328.    * Resize the linestyle matrix, the symbolstyle matrix, and
  329.    * the legend form.
  330.    * Also arrange to select and highlight only the top button in
  331.    * each column of the linestyle and symbolstyle matrices.
  332.    * This code could stand to be cleaned up.
  333.    */
  334.   char formtitle[80];
  335.   int j, k;
  336.  
  337.   if (oldn >= newn) {    /* have to delete some columns and revise the remaining */
  338.     for (j=oldn-1; j>=newn; j--) {
  339.       [lineText removeColAt:j andFree:YES]; /* Titles on Columns  */
  340.       [symbolText removeColAt:j andFree:YES]; /* Titles on Columns  */
  341.       [lineMatrix removeColAt:j andFree:YES];
  342.       [symbolMatrix removeColAt:j andFree:YES];
  343.       [legendForm removeEntryAt:j];
  344.     }
  345.     for (j=0; j<newn; j++) {
  346.       if ( [ [lineMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
  347.     [ [lineMatrix cellAt:0 :j] setState:1];
  348.       if ( [ [symbolMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
  349.     [ [symbolMatrix cellAt:0 :j] setState:1];
  350.       for (k=1; k<N_LINE_STYLES; k++) {
  351.     [ [lineMatrix cellAt:k :j] setState:0];
  352.       }
  353.       for (k=1; k<N_SYMBOL_STYLES; k++) {
  354.     [ [symbolMatrix cellAt:k :j] setState:0];
  355.       }
  356.       sprintf(formtitle, "Curve %d", j+1);
  357.       [legendForm setStringValue:formtitle at:j];
  358.       [legendForm drawCellAt:j];
  359.       sprintf(formtitle, "%d", j+1);
  360.       [[lineText cellAt:0 :j] setStringValue:formtitle];
  361.       [[symbolText cellAt:0 :j] setStringValue:formtitle];
  362.     }
  363.     [legendTitle setStringValue:"Legend" at:0];
  364.     [lineText sizeToCells];    /* Titles on Columns */
  365.     [symbolText sizeToCells];    /* Titles on Columns */
  366.     [lineMatrix sizeToCells];
  367.     [symbolMatrix sizeToCells];
  368.     [legendForm sizeToFit];
  369.     [ [lineText window] display];   /* Titles on Columns*/
  370.     [ [symbolText window] display]; /* Titles on Columns*/
  371.     [ [lineMatrix window] display];   /* redraw the whole window so extra */
  372.     [ [symbolMatrix window] display]; /* columns get erased               */
  373.     [ [legendForm window] display];
  374.   }
  375.   if ( (oldn == 0) && (newn > 1) ) { /* can only happen first time */
  376.     for (j=1; j<newn; j++) {
  377.       [lineText addCol];
  378.       [symbolText addCol];
  379.       [lineMatrix addCol];
  380.       if ( [ [lineMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
  381.     [ [lineMatrix cellAt:0 :j] setState:1];
  382.       /* highlight 1st row new column -- this occurs automatically */
  383.       [symbolMatrix addCol];
  384.       if ( [ [symbolMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
  385.     [ [symbolMatrix cellAt:0 :j] setState:1];
  386.       /* highlighting of 1st row new column occurs automatically */
  387.       sprintf(formtitle, "Curve %d:", j+1);
  388.       [legendForm addEntry:formtitle];
  389.       sprintf(formtitle, "Curve %d", j+1);
  390.       [legendForm setStringValue:formtitle at:j];
  391.       [legendForm drawCellAt:j];
  392.       sprintf(formtitle, "%d", j+1);
  393.       [[lineText cellAt:0 :j] setStringValue:formtitle];
  394.       [[symbolText cellAt:0 :j] setStringValue:formtitle];
  395.     }
  396.     [lineText sizeToCells];    /* Titles on Columns */
  397.     [symbolText sizeToCells];    /* Titles on Columns */
  398.     [lineMatrix sizeToCells];
  399.     [symbolMatrix sizeToCells];
  400.     [legendForm sizeToFit];
  401.     [lineText display];
  402.     [symbolText display];
  403.     [lineMatrix display];    /* don't need to redraw the window here */
  404.     [symbolMatrix display];
  405.     [legendForm display];
  406.   }
  407.   if ( (oldn != 0) && (oldn < newn) ) { /* must add columns */
  408.     for (j=0; j<oldn; j++) {
  409.       if ( [ [lineMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
  410.     [ [lineMatrix cellAt:0 :j] setState:1];
  411.       if ( [ [symbolMatrix cellAt:0 :j] state ] != 1 ) /* seems necessary */
  412.     [ [symbolMatrix cellAt:0 :j] setState:1];
  413.       for (k=1; k<N_LINE_STYLES; k++) {
  414.     [ [lineMatrix cellAt:k :j] setState:0];
  415.       }
  416.       for (k=1; k<N_SYMBOL_STYLES; k++) {
  417.     [ [symbolMatrix cellAt:k :j] setState:0];
  418.       }
  419.       sprintf(formtitle, "Curve %d", j+1);
  420.       [legendForm setStringValue:formtitle at:j];
  421.       [legendForm drawCellAt:j];
  422.       sprintf(formtitle, "%d", j+1);
  423.       [[lineText cellAt:0 :j] setStringValue:formtitle];
  424.       [[symbolText cellAt:0 :j] setStringValue:formtitle];
  425.     }
  426.     for (j=oldn; j<newn; j++) {
  427.       [lineText addCol];
  428.       [symbolText addCol];
  429.       [lineMatrix addCol];
  430.       [ [lineMatrix cellAt:0 :j] setState:1];
  431.       /* highlighting of 1st row new column occurs automatically */
  432.       [symbolMatrix addCol];
  433.       [ [symbolMatrix cellAt:0 :j] setState:1];
  434.       /* highlighting of 1st row new column occurs automatically */
  435.       sprintf(formtitle, "Curve %d:", j+1);
  436.       [legendForm addEntry:formtitle];
  437.       sprintf(formtitle, "Curve %d", j+1);
  438.       [legendForm setStringValue:formtitle at:j];
  439.       [legendForm drawCellAt:j];
  440.       sprintf(formtitle, "%d", j+1);
  441.       [[lineText cellAt:0 :j] setStringValue:formtitle];
  442.       [[symbolText cellAt:0 :j] setStringValue:formtitle];
  443.     }
  444.     [legendTitle setStringValue:"Legend" at:0];
  445.     [lineText sizeToCells];    /* Titles on Columns */
  446.     [symbolText sizeToCells];    /* Titles on Columns */
  447.     [lineMatrix sizeToCells];
  448.     [symbolMatrix sizeToCells];
  449.     [legendForm sizeToFit];
  450.     [lineText display];        /* Titles on Columns */
  451.     [symbolText display];    /* Titles on Columns */
  452.     [lineMatrix display];    /* don't need to redraw the window here */
  453.     [symbolMatrix display];
  454.     [legendForm display];
  455.   }
  456.   [lineThickness setState:1 at:0:0]; /* reset line thickness to thin */
  457.   [symbolSize setState:1 at :0:2];   /* reset symbol size to medium */
  458.   [symbolSize display];
  459.   [lineThickness display];
  460.   return self;
  461. }
  462.  
  463. // Go through the data set and find values for datamin.x,
  464. // datamax.x, datamin.y, datamax.y
  465. - findMinAndMax
  466. {
  467.     if (x)  {
  468.       int    i, j;
  469.       datamin.x = datamax.x = x[0];
  470.       for (i = 1; i < npoints; i++)  {
  471.     if (x[i] < datamin.x)
  472.       datamin.x = x[i];
  473.     else if (x[i] > datamax.x)
  474.       datamax.x = x[i];
  475.       }
  476.       datamin.y = datamax.y = *(*(y+0)+0);
  477.       for (j = 0; j < ncurves; j++) {
  478.     for (i = 0; i < npoints; i++) {
  479.       if (*(*(y+j)+i) < datamin.y)
  480.         datamin.y = *(*(y+j)+i);
  481.       else if (*(*(y+j)+i) > datamax.y)
  482.         datamax.y = *(*(y+j)+i);
  483.     }
  484.       }
  485.     }
  486. // reset min and max here (may want to change the placement of this)
  487.     [self resetXmin:datamin.x];
  488.     [self resetXmax:datamax.x];
  489.     [self resetXinc:(datamax.x - datamin.x)/5.0];
  490.     [self resetYmin:datamin.y];
  491.     [self resetYmax:datamax.y];
  492.     [self resetYinc:(datamax.y - datamin.y)/5.0];
  493.  
  494.     return self;
  495. }
  496.  
  497.  
  498. // Use the OpenPanel object to get a filename
  499. - open:sender
  500. {
  501.     char const    *fileTypes[2] = {0,0};    // this type is all ASCII files (?)
  502. //    char const    *fileTypes[2] = {"xyp",0};
  503. //          this would be only files with an xyp extension
  504.     char    fname[1024];
  505.  
  506.     id openPanel = [OpenPanel new];
  507.     if ([openPanel runModalForTypes:fileTypes])  {
  508.       strncpy(fname, [openPanel filename], 1024);
  509.       [self openFile:fname];
  510.     }
  511.     return self;
  512. }
  513.  
  514. - openFile:(char *)dataFile
  515. {
  516.     FILE    *dataStream;
  517.  
  518.     if ((dataStream = fopen(dataFile, "r")) == NULL)  {
  519.       NXRunAlertPanel("Open", "Cannot open %s", "OK", NULL, NULL, dataFile);
  520.       return self;
  521.     }
  522.  
  523.     if ([self readData:dataStream] == 0)  {
  524.       NXRunAlertPanel("Read", "Couldn't read any data from %s", "OK",
  525.               NULL, NULL, dataFile);
  526.       fclose(dataStream);
  527.       return self;
  528.     }
  529.     fclose(dataStream);
  530.  
  531.     [canvas initializeLegendBox];
  532.  
  533.     [self findMinAndMax];
  534.  
  535.     /* Check for linear or log on x and y axes */
  536.     [self checkLinLog];
  537.  
  538.     [self drawPlot:self];
  539.     
  540.     return self;
  541. }
  542.  
  543. @end
  544.